17 Cours GUI
Les interfaces graphiques (GUI : graphical user interfaces en anglais) permettent d’étendre les possibilités de la programmation en Python.
La bibliothèque Tkinter est la plus connue.
Il en existe d’autres comme Pygame ou PySimpleGUI.
Tkinter⚓︎
Si elle n'est pas installée chez vous, on installe la bibliothèque en tapant pip install tkinter dans la console.
On charge ensuite cette bibliothèque en entrant : from tkinter import *
Introduction⚓︎
Pour créer un logiciel graphique, il faut tout d’abord créer une fenêtre. On ajoute ensuite dans une fenêtre des éléments graphiques que l'on nomme widget. Quelques exemples de widget :
Button: proposer une action à l'utilisateur.Label: espaces prévus pour écrire du texte.Canvas: espace dans lequel vous pouvez dessiner ou écrireFrame: cadres qui permettent de séparer des éléments.
Chaque widget est une fonction qui demande arguments spécifiques, ainsi que des options (taille, couleur, etc…).
Dans chaque cas, on assigne une variable au widget, afin de le repérer, et on le « compile » dans la fenêtre. Il faut enfin afficher la fenêtre.
| Widget | Description |
|---|---|
fenetre = Tk() |
Créer une fenêtre |
bouton = Button(fenetre, text = "Texte à afficher", command = Fonction) |
Créer un bouton \(^{(1)}\) |
label = Label(fenetre, text = "Texte à afficher") |
Créer un label |
canevas = Canvas(fenetre, width= Taille en px, height= Taille en px) |
Créer un canevas |
cadre = Frame(fenetre) |
Créer un cadre |
NomWidget.pack() |
Ajouter le widget |
NomFenetre.mainloop() |
Afficher la fenêtre |
\(^{(1)}\) Pour Button, il est possible de faire passer des arguments dans la fonction avec lambda : command = lambda :Fonction(arg).
Exemple 1⚓︎
L’exemple suivant crée une fenêtre avec saisie, affichage et modification de texte. Elle figure sous le nom tkinter ou tk (dans la barre des tâches sous Windows, idem pour les autres systèmes).
from tkinter import * # import de la bibliothèque graphique
######### FONCTION
def afficher(event):
texte2.configure(text = "Bonjour " + texte_saisi.get())
return()
######### PROGRAMME PRINCIPAL
# On crée une fenêtre, racine de notre interface
fenetre = Tk()
# Titre de la fenêtre
fenetre.title("Ma première fenêtre")
# Taille minimale de la fenêtre
fenetre.minsize(width = 250, height = 50)
# On crée un texte (ligne de texte) de titre
texte = Label(fenetre, text = "Hello world !")
texte.pack() # On affiche le texte dans la fenêtre, centré horizontalement par defaut
# On crée un canevas, sur lequel on pourra dessiner, par exemple
can = Canvas(fenetre, width =100, height =100, bg = 'LightGreen' ) #on crée un canevas pour la fenêtre
can.pack() #on ajoute le canevas dans la fenêtre
# On peut dessiner des lignes ou des cercles sur le canevas :
can.create_line(50, 0, 50, 100, width=3, fill = 'red' )
can.create_line(0, 50, 100, 50, width=3, fill = 'red' )
rayon = 10
can.create_oval(75-rayon, 75-rayon, 75+rayon, 75+rayon, outline='black', fill="blue")
# On affiche un texte, puis un champ de saisie
texte2 = Label(fenetre, text = "Entrez votre prénom")
texte2.pack(side = LEFT) # On affiche le texte dans la fenêtre, à gauche
texte_saisi = Entry(fenetre, width = 30) # Le champ de saisie donne une variable nommée texte_saisi
texte_saisi.bind("<Return>", afficher) # Quand on appuie sur entrée, on appelle fonction "afficher"
texte_saisi.pack() # On affiche le texte dans la fenêtre
# On rajoute un bouton "quitter", avec une commande fenetre.destroy au nom assez explicite !
bouton_quitter = Button(fenetre, text = "Quitter", command = fenetre.destroy)
bouton_quitter.pack() # On affiche le bouton dans la fenêtre
# On démarre la boucle Tkinter qui s'interrompt quand on ferme la fenêtre
fenetre.mainloop()

Dessiner dans un canevas :⚓︎
Il existe différentes fonctions pour dessiner dans un canevas. Il faut alors préciser dans quel canevas faire le dessin en début du nom de la fonction.
Quelques fonctions sont listées ci-dessous :
| Fonction | Description |
|---|---|
canevas.create_line(x1, y1, x2, y2) |
Dessiner une ligne entre le point de coordonnées (x1, y1) et le point de coordonnées (x2, y2) |
canevas.create_arc(x1, y1, x2, y2) |
Dessiner un arc entre le point de coordonnées (x1, y1) et le point de coordonnées (x2, y2) |
canevas.create_rectangle(x1, y1, x2, y2) |
Dessiner un rectangle défini par 2 coins donnés par les points de coordonnées (x1, y1) et (x2, y2) |
canevas.create_oval(x1, y1, x2, y2) |
Dessiner un cercle dans le rectangle défini par 2 coins donnés par les points de coordonnées (x1, y1) et (x2, y2) |
Interactions⚓︎
Il existe plusieurs formes d'intéraction avec l'utilisateur : des boutons, des zines de saisie de texte, des réactions lors de clic de souris ou de touches pressées sur le clavier, etc.
Il faut créer le widget, et il faut relier les événements aux actions.
-
pour un bouton, on définit la commande à exécuter directement dans la définition du bouton :
bouton = Button(fenetre, text = "Texte à afficher", command = Fonction) -
pour une fenêtre de saisie :
entree = Entry(fenetre)
on pourra définir la fonction à exécuter lorsqu'on appuie sur le bouton "Return" :entree.bind("<Return>", Fonction)
Le contenu de la zone de saisi sera utilisé avecentree.get()
Les actions utilisateurs (clic de souris, touche de clavier appuyée…) peuvent être récupérées à travers les évènements, donnés dans la variable event. Cette variable est obligatoire en seul argument lorsque l'on définit une fonction gestionnaire d'évènements qui est associée à un widget par la méthode bind. On doit alors l'utiliser comme premier argument. Cet argument désigne un objet créé automatiquement par tkinter, qui permet de transmettre au gestionnaire d'événement un certain nombre d'attributs de l'événement :
- le type d'événement : déplacement de la souris, enfoncement ou relâchement de l'un de ses boutons, appui sur une touche du clavier, entrée du curseur dans une zone prédéfinie, ouverture ou fermeture d'une fenêtre, etc…
- une série de propriétés de l'événement : l'instant où il s'est produit, ses coordonnées, les caractéristiques du ou des widget(s) concerné(s), etc…
Une liste non-exhaustive des différents évènements est donnée ci-dessous :
| Code | Événement |
|---|---|
<Button-1> |
Clic gauche |
<Button-2> |
Clic milieu |
<Button-3> |
Clic droit |
<Double-Button-1> |
Double clic gauche |
<KeyPress> |
Pression sur une touche |
<KeyPress-a> |
Pression sur la touche a minuscule |
<KeyPress-A> |
Pression sur la touche A majuscule |
<Return> |
Pression sur la touche Entrer |
<Up> |
Pression sur la touche flèche vers le haut |
<Down> |
Pression sur la touche flèche vers le bas |
Placer les éléments sur la fenêtre⚓︎
Vous remarquez que les éléments de l’interface graphique (widgets) sont positionnés les uns au dessus des autres, dans l’ordre de leur apparition dans le programme. Diverses méthodes permettent de mieux préciser l’emplacement des différents composants de l’interface.
Exemple 2⚓︎
Version 1⚓︎
L’exemple suivant, basé sur la conversion Farenheit/Celsius, montre l’usage d’une grille (grid en anglais).
from tkinter import *
##### FONCTION
def convertir(event=None):
"""
Convertit une temperature de Fahrenheit en Celsius
"""
fahr = float(temperature.get())
celsius = 5 / 9 * (fahr - 32)
texte3.config(text = round(celsius, 2))
##### PROGRAMME PRINCIPAL
fenetre = Tk() # On crée une fenêtre, racine de notre interface
# On crée tous les textes ainsi que tous les éléments de la fenêtre
fenetre.title("Utilitaire de conversion de F à C")
texte2 = Label(fenetre, text ="Entrez une temperature en degres Farenheit")
texte3 = Label(fenetre, text = "")
temperature = Entry(fenetre)
temperature.bind("<Return>", convertir)
bouton_quitter = Button(fenetre, text = "Quitter", command = fenetre.destroy)
# Un bouton qui appelle la fonction convertir et redéfinit le contenu du texte3
bouton_convertir = Button(fenetre, text = "Convertir en degres Celsius", command = convertir)
# On affiche tout, avec la methode grid qui range les elements dans des lignes et colonnes
bouton_convertir.grid(row = 0, column = 1)
texte2.grid(row = 1, sticky = W)
texte3.grid(row = 2,sticky = W)
temperature.grid(row = 1, column = 1)
bouton_quitter.grid(row = 2, column = 1)
# On démarre la boucle Tkinter qui s'interrompt quand on ferme la fenêtre
fenetre.mainloop()
Version 2⚓︎
from tkinter import *
def convertir(event=None):
fahrenheit = float(temp_fahr.get())
celsius = (fahrenheit - 32) * 5 / 9
temp_celcius.config(text = round(celsius, 2))
fenetre = Tk()
fenetre.title("Convertisseur de température F à C")
fenetre.minsize(width = 350, height = 100)
temp_fahr = Entry(justify = RIGHT)
temp_celcius = Label(text = "", justify = RIGHT, width = 8)
action = Button(text = 'Convertir', command = convertir)
temp_fahr.pack(padx = 20, pady = 20)
temp_celcius.pack(padx = 20, pady = 20, side = RIGHT)
action.pack(padx = 20, pady = 20)
fenetre.mainloop()
PySimpleGUI⚓︎
Ce paquet PySimpleGUI n'est pas installé de base, il faut l'installer spécifiquement (python3 -m pip install PySimpleGUI).
import PySimpleGUI as sg
def fenetreDeSaisie():
affichage = [
[sg.Text('Veuillez entrer votre nom, votre adresse et votre numéro de téléphone')],
[sg.Text('Nom', size=(15, 1)), sg.InputText('nom')],
[sg.Text('Adresse', size=(15, 1)), sg.InputText('adresse')],
[sg.Text('Téléphone', size=(15, 1)), sg.InputText('telephone')],
[sg.Submit('Soumettre'), sg.Cancel('Annuler')]
]
fenetre = sg.Window('Exemple de base').Layout(affichage)
button, values = fenetre.Read()
print("Dans la fonction :", button, '\n', values[0], values[1], values[2])
return values
values = fenetreDeSaisie()
print("Après fermeture :", values[0], values[1], values[2])
Pygame⚓︎
Ce paquet Pygame n'est pas installé de base, il faut l'installer spécifiquement (python3 -m pip install pygame).
Exemple 1⚓︎
"""
Simple graphics demo
Sample Python/Pygame Programs
Simpson College Computer Science
http://programarcadegames.com/
http://simpson.edu/computer-science/
"""
# Import a library of functions called 'pygame'
import pygame
# Initialize the game engine
pygame.init()
# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
PI = 3.141592653
# Set the height and width of the screen
size = (400, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Rotate Text")
# Loop until the user clicks the close button.
done = False
clock = pygame.time.Clock()
text_rotate_degrees = 0
# Loop as long as done == False
while not done:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
# All drawing code happens after the for loop and but
# inside the main while not done loop.
# Clear the screen and set the screen background
screen.fill(WHITE)
# Draw some borders
pygame.draw.line(screen, BLACK, [100,50], [200, 50])
pygame.draw.line(screen, BLACK, [100,50], [100, 150])
# Select the font to use, size, bold, italics
font = pygame.font.SysFont('Calibri', 25, True, False)
# Sideways text
text = font.render("Sideways text", True, BLACK)
text = pygame.transform.rotate(text, 90)
screen.blit(text, [0, 0])
# Sideways text
text = font.render("Upside down text", True, BLACK)
text = pygame.transform.rotate(text, 180)
screen.blit(text, [30, 0])
# Flipped text
text = font.render("Flipped text", True, BLACK)
text = pygame.transform.flip(text, False, True)
screen.blit(text, [30, 20])
# Animated rotation
text = font.render("Rotating text", True, BLACK)
text = pygame.transform.rotate(text, text_rotate_degrees)
text_rotate_degrees += 1
screen.blit(text, [100, 50])
# Go ahead and update the screen with what we've drawn.
# This MUST happen after all the other drawing commands.
pygame.display.flip()
# This limits the while loop to a max of 60 times per second.
# Leave this out and we will use all CPU we can.
clock.tick(60)
# Be IDLE friendly
pygame.quit()
Exemple 2⚓︎
"""
Bounces a rectangle around the screen.
Sample Python/Pygame Programs
Simpson College Computer Science
http://programarcadegames.com/
http://simpson.edu/computer-science/
Explanation video: http://youtu.be/-GmKoaX2iMs
"""
import pygame
# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
pygame.init()
# Set the height and width of the screen
size = [700, 500]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Bouncing Rectangle")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Starting position of the rectangle
rect_x = 50
rect_y = 50
# Speed and direction of rectangle
rect_change_x = 2
rect_change_y = 2
# -------- Main Program Loop -----------
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Logic
# Move the rectangle starting point
rect_x += rect_change_x
rect_y += rect_change_y
# Bounce the ball if needed
if rect_y > 450 or rect_y < 0:
rect_change_y = rect_change_y * -1
if rect_x > 650 or rect_x < 0:
rect_change_x = rect_change_x * -1
# --- Drawing
# Set the screen background
screen.fill(BLACK)
# Draw the rectangle
pygame.draw.rect(screen, WHITE, [rect_x, rect_y, 50, 50])
pygame.draw.rect(screen, RED, [rect_x + 10, rect_y + 10, 30, 30])
# --- Wrap-up
# Limit to 60 frames per second
clock.tick(60)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Close everything down
pygame.quit()
Streamlit⚓︎
Ce paquet streamlit n'est pas installé de base, il faut l'installer spécifiquement (python3 -m pip install streamlit).
Pour exécuter le code, il faut taper streamlit run mon_fichier.py en ligne de commande.
import streamlit as st
# To make things easier later, we're also importing numpy and pandas for
# working with sample data.
import numpy as np
import pandas as pd
import time
st.title("My first app")
st.write("Here's our first attempt at using data to create a table:")
st.write(
pd.DataFrame({"first column": [1, 2, 3, 4], "second column": [10, 20, 30, 40]})
)
"""
# My first app
Here's our first attempt at using data to create a graph:
"""
df = pd.DataFrame({"first column": [1, 2, 3, 4], "second column": [10, 20, 30, 40]})
chart_data = pd.DataFrame(np.random.randn(20, 3), columns=["a", "b", "c"])
st.line_chart(chart_data)
map_data = pd.DataFrame(
np.random.randn(1000, 2) / [50, 50] + [37.76, -122.4], columns=["lat", "lon"]
)
st.map(map_data)
if st.checkbox("Show dataframe"):
chart_data = pd.DataFrame(np.random.randn(20, 3), columns=["a", "b", "c"])
st.line_chart(chart_data)
option = st.selectbox("Which number do you like best?", df["first column"])
"You selected: ", option
left_column, right_column = st.beta_columns(2)
pressed = left_column.button("Press me?")
if pressed:
right_column.write("Woohoo!")
expander = st.beta_expander("FAQ")
expander.write("Here you could put in some really, really long explanations...")
"Starting a long computation..."
# Add a placeholder
latest_iteration = st.empty()
bar = st.progress(0)
for i in range(100):
# Update the progress bar with each iteration.
latest_iteration.text(f"Iteration {i+1}")
bar.progress(i + 1)
time.sleep(0.1)
"...and now we're done!"